package data;

import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import prefuse.data.Table;
import prefuse.data.column.Column;
import prefuse.data.io.CSVTableReader;
import prefuse.data.io.DataIOException;
import prefuse.data.parser.BooleanParser;
import prefuse.data.parser.ColorIntParser;
import prefuse.data.parser.DataParser;
import prefuse.data.parser.DoubleArrayParser;
import prefuse.data.parser.DoubleParser;
import prefuse.data.parser.FloatArrayParser;
import prefuse.data.parser.FloatParser;
import prefuse.data.parser.IntArrayParser;
import prefuse.data.parser.IntParser;
import prefuse.data.parser.LongArrayParser;
import prefuse.data.parser.LongParser;
import prefuse.data.parser.ParserFactory;
import prefuse.data.parser.StringParser;
import prefuse.data.util.Sort;
import prefuse.visual.VisualTable;

/**
 * Provides XC data in a singleton.
 */
public class DataManager {
	
	private static DataManager instance;
	private Table dataTable;
	private Table originalTable;
	private Map<String, Integer> meetOffset;
	private String[] sortOrder;
	
	
	public static DataManager getInstance() {
		if (instance == null) {
			instance = new DataManager();
		}
		return instance;
	}
	
	private static final DataParser[] PARSERS = 
        new DataParser[] {
            new IntParser(),
            new LongParser(),
            new DoubleParser(),
            new FloatParser(),
            new BooleanParser(),
            new ColorIntParser(),
//            new DateParser(),
//            new TimeParser(),
//            new DateTimeParser(),
            new IntArrayParser(),
            new LongArrayParser(),
            new FloatArrayParser(),
            new DoubleArrayParser(),
            new StringParser()
        };
	
	private double timeInSeconds(final String time) {
//		System.out.print(time + " -> ");
		String[] timePieces = time.split(":");
		double seconds = Double.parseDouble(timePieces[0]) * 60 + Double.parseDouble(timePieces[1]);
//		System.out.println(seconds);
		return seconds;
	}
	private String timeToString(final Double time) {
		DecimalFormat df = new DecimalFormat("#.##");
		int minutes = (int)(time / 60);
		Double seconds = time - minutes*60;
		String secondsString = df.format(seconds);
		if (seconds < 10) {
			secondsString = "0"+df.format(seconds);
		}
		String minutesString = minutes+"";
		if (minutes < 10) {
			minutesString = "0"+minutes;
		}
		return minutesString + ":" + secondsString;
	}
	
	public static Integer getOffset(final String meet) {
		return DataManager.getInstance().meetOffset.get(meet);
	}
	
	public static void setOffset(final String meet, final int offset) {
		DataManager.getInstance().meetOffset.put(meet, offset);
		DataManager.getInstance().updateTable();
	}
	public void clearOffsets() {
		Set<Object> meets = getUniqueColumnEntries("Meet");
		for (Object meet : meets) {
			meetOffset.put(meet.toString(), 0);
		}
	}
	private void updateTable() {
		for (int i = 0; i < dataTable.getRowCount(); i++) {
			Double newValue = timeInSeconds(originalTable.getString(i, "Time"))
					+ meetOffset.get(originalTable.get(i, "Meet"));
			dataTable.set(i, "Time", timeToString(newValue));
		}
	}
	
	private DataManager() {
//		System.out.println(timeToString(timeInSeconds("18:05")));
//		System.exit(0);
		dataTable = null;
		originalTable = null;
		meetOffset = new HashMap<String, Integer>();
		try {
			originalTable = new CSVTableReader(new ParserFactory()).readTable("xc_data.csv");
			// is there a better way to clone?
			dataTable = new CSVTableReader(new ParserFactory()).readTable("xc_data.csv");
			clearOffsets();
			updateTable();
			
		} catch (final DataIOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public void setSortOrder(final String[] sortOrder) {
		this.sortOrder = sortOrder;
	}
	
	public Table getTable() {
		if (sortOrder == null) {
			return dataTable;
		}
		return getSortedTable(new Sort(sortOrder));
	}
	
	public Table getSortedTable(Sort sort) {
		return dataTable.select(null, sort);
	}
	
	public HashSet<Object> getUniqueColumnEntries(final String columnName) {
		Column col = dataTable.getColumn(columnName);
		HashSet<Object> uniqueSet = new LinkedHashSet<Object>();
		for (int i=0; i<col.getRowCount(); i++) {
			uniqueSet.add(col.get(i));
		}
		return uniqueSet;
	}
	
	public String[] getRunnerData(final String runner) {
		boolean first = true;
		String name = "", school = "", year = "";
		Double avgTime = 0.0;
		Double avgPlace = 0.0;
		double entries = 0;
		for (int i=0; i< dataTable.getRowCount(); i++) {
			if (dataTable.get(i, "Name").equals(runner)) {
				entries++;
				if (first) {
					name = runner; 
					school = dataTable.getString(i, "School");
					year = dataTable.getString(i, "Year");
					first = false;
				}
				avgTime += timeInSeconds(dataTable.getString(i, "Time"));
				avgPlace += dataTable.getInt(i, "Place");
			}
		}
		DecimalFormat df = new DecimalFormat("#.##");
		String averagePlace = entries == 0 ? "" : df.format(((Double)(avgPlace/entries)));
		String avgTimeStr = entries == 0 ? "" : timeToString(avgTime/entries);
		String[] retVal = {name, school, year, avgTimeStr, averagePlace};
		return retVal;
	}
	
	/**
	 * This is a hack.
	 */
	public HashSet<Object> getDullesTeams() {
		Column col = dataTable.getColumn("School");
		HashSet<Object> uniqueSet = new LinkedHashSet<Object>();
		for (int i=0; i<col.getRowCount(); i++) {
			if (dataTable.getString(i, "District").equals("Dulles")) {
				uniqueSet.add(col.get(i));
			}
		}
		return uniqueSet;
	}
	
	public HashSet<Object> getUniqueColumnEntries(final String columnName, final VisualTable table) {
		Column col = table.getColumn(columnName);
		HashSet<Object> uniqueSet = new LinkedHashSet<Object>();
		for (int i=0; i<col.getRowCount(); i++) {
			if (table.getChildRow(i) != -1) {
				uniqueSet.add(col.get(i));
			}
		}
		return uniqueSet;
	}
}
